﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ExpressionTreeDemo.ExpressionVisitors
{
    public class ConditionBuilderVisitor : ExpressionVisitor
    {
        private readonly Stack<string> _stringStack = new Stack<string>();

        public ConditionBuilderVisitor(Expression expression)
        {
            Visit(expression);
            Condition();
        }

        public void Condition()
        {
            string condition = string.Concat(_stringStack.ToArray());
            _stringStack.Clear();
            Console.WriteLine(condition);
        }

        public override Expression Visit(Expression node)
        {
            Console.WriteLine($"Visit:{node.ToString()}");
            return base.Visit(node);
        }

        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node == null) throw new ArgumentNullException("BinaryExpression");

            _stringStack.Push(")");

            base.Visit(node.Right);

            _stringStack.Push(" " + ToSqlOperator(node.NodeType) + " ");

            base.Visit(node.Left);

            _stringStack.Push("(");

            return node;
        }

        private string ToSqlOperator(ExpressionType type)
        {
            switch (type)
            {
                case (ExpressionType.AndAlso):
                case (ExpressionType.And):
                    return "AND";
                case (ExpressionType.OrElse):
                case (ExpressionType.Or):
                    return "OR";
                case (ExpressionType.Not):
                    return "NOT";
                case (ExpressionType.NotEqual):
                    return "<>";
                case ExpressionType.GreaterThan:
                    return ">";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case (ExpressionType.Equal):
                    return "=";
                default:
                    throw new Exception("不支持该方法");
            }
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node == null) throw new ArgumentNullException("MemberExpression");

            _stringStack.Push(" [" + node.Member.Name + "] ");
            return node;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node == null) throw new ArgumentNullException("ConstantExpression");

            _stringStack.Push(" '" + node.Value + "' ");
            return node;
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m == null) throw new ArgumentNullException("MethodCallExpression");

            string format;
            switch (m.Method.Name)
            {
                case "StartsWith":
                    format = "({0} LIKE {1}+'%')";
                    break;

                case "Contains":
                    format = "({0} LIKE '%'+{1}+'%')";
                    break;

                case "EndsWith":
                    format = "({0} LIKE '%'+{1})";
                    break;

                default:
                    throw new NotSupportedException(m.NodeType + " is not supported!");
            }

            Visit(m.Object);
            Visit(m.Arguments[0]);
            string right = _stringStack.Pop();
            string left = _stringStack.Pop();
            _stringStack.Push(string.Format(format, left, right));
            return m;
        }
    }
}
